import { GameConfig } from "../configs/GameConfig"
import { Attribute } from "../etc/Attribute"
import { Easing } from "../etc/Tween"
import { UIPool } from "../etc/UIPool"
import { updater } from "../etc/Updater"
import { Tween } from "../etc/Tween"
import { ItemBaseInfo } from "../module/ItemModule"
import MiddleNoticeItem_Generate from "../ui-generate/notice/MiddleNoticeItem_generate"
import NoticeView_Generate from "../ui-generate/notice/NoticeView_generate"
import SecondNoticeItem_Generate from "../ui-generate/notice/SecondNoticeItem_generate"
import TopNoticeItem_Generate from "../ui-generate/notice/TopNoticeItem_generate"
import { util } from "../etc/Util"


export class Notice {
    private static view: NoticeView

    static showTopNotice(context: string, txtHex = "#FBFFFF") {
        this.checkView()
        this.view.topNoticeComponent.insert(notice => {
            notice.setInfo(context, txtHex)
        })
    }

    static showSecondWithIcon(icon: string, context: string) {
        this.checkView()
        this.view.secondNoticeComponent.insert(notice => {
            notice.setInfo(icon, context)
        })
    }

    static showSecondWithAssetsIcon(icon: string, context: string) {
        this.checkView()
        this.view.secondNoticeComponent.insert(notice => {
            notice.setInfoWithAssetsIcon(icon, context)
        })
    }

    // 屏蔽secondNotice推送
    static shieldSecondAndMiddleNotice() {
        this.checkView()
        this.view.secondNoticeComponent.shield = true
        this.view.middleNoticeComponent.shield = true
    }

    // 解除屏蔽secondNotice推送
    static unShieldSecondAndMiddleNotice() {
        this.checkView()
        this.view.secondNoticeComponent.shield = false
        this.view.middleNoticeComponent.shield = false
    }

    private static checkView() {
        if (this.view) return
        this.view = UIService.show(NoticeView)
    }
}

interface NoticeComponent<T> {
    init(targetCanvas: Canvas)
    insert(initAction: (item: T) => void)
    update()
}

type InitTopNoticeFunction = (item: TopNoticeItem) => void
type InitSecondNoticeFunction = (item: SecondNoticeItem) => void
type InitMiddleNoticeFunction = (item: MiddleNoticeItem) => void
class TopNoticeComponent implements NoticeComponent<TopNoticeItem>{
    private static NoticeItemLifeTime = 2
    private static NoticeItemMaxCount = 3
    private static NoticeMoveStepCount = 15
    private static NoticeItemIntervalSpace = 75

    private noticeItemPool: UIPool<TopNoticeItem>
    private visibleNotice: TopNoticeItem[]
    private noticeCanvasHeight: number
    private targetCanvas: Canvas
    private pendingQueue: InitTopNoticeFunction[]
    private insertItemTempLocation: Vector2

    init(targetCanvas: Canvas) {
        this.visibleNotice = []
        this.pendingQueue = []
        this.targetCanvas = targetCanvas
        this.noticeCanvasHeight = this.targetCanvas.size.y
        this.insertItemTempLocation = new Vector2()
        this.noticeItemPool = new UIPool<TopNoticeItem>(() => {
            let item = UIService.create(TopNoticeItem)
            this.targetCanvas.addChild(item.uiObject)
            item.uiObject.size = new Vector2(700, 60)
            return item
        })
    }

    insert(initAction: InitTopNoticeFunction) {
        this.pendingQueue.push(initAction)
    }

    update() {
        if (this.visibleNotice.length == 0) return
        for (let item of this.visibleNotice) { item.lifeTime += 0.03 }
        let first = this.visibleNotice[0]
        if (first.lifeTime >= TopNoticeComponent.NoticeItemLifeTime) { this.fadeoutNoticeElement() }

        this.noticeItemPool.eachVisibleItem(item => {
            if (item.targetHeight >= item.position.y) return
            item.setLocation(item.position.x, item.position.y - TopNoticeComponent.NoticeMoveStepCount)
        })
    }

    private insertPendingNotice(initAction: InitTopNoticeFunction) {
        // 超出显示长度,旧的元素隐藏
        if (this.visibleNotice.length >= TopNoticeComponent.NoticeItemMaxCount) { this.fadeoutNoticeElement() }
        // 已显示元素上推
        for (let i = 0; i < this.visibleNotice.length; i++) {
            const element = this.visibleNotice[i]
            element.targetHeight =
                (this.noticeCanvasHeight - TopNoticeComponent.NoticeItemIntervalSpace) -
                ((this.visibleNotice.length - i) * TopNoticeComponent.NoticeItemIntervalSpace)
            element.stopEffect()
        }
        // 插入新的元素
        let recent = this.noticeItemPool.get()
        this.visibleNotice.push(recent)
        initAction(recent)
        recent.lifeTime = 0
        this.insertItemTempLocation.x = (this.targetCanvas.size.x / 2) - (recent.uiObject.size.x / 2)
        this.insertItemTempLocation.y = this.targetCanvas.size.y - TopNoticeComponent.NoticeItemIntervalSpace
        recent.setLocation(this.insertItemTempLocation.x, this.insertItemTempLocation.y)
        recent.targetHeight = this.insertItemTempLocation.y
        recent.uiObject.renderOpacity = 0;
        recent.playEffect()
        // 插入动效
        new Tween<{ alpha: number }>({ alpha: 0 })
            .to({ alpha: 1 }, 250)
            .onUpdate(arg => {
                recent.uiObject.renderOpacity = arg.alpha;
            })
            .start()
    }

    private fadeoutNoticeElement() {
        let item = this.visibleNotice.shift()
        new Tween<{ alpha: number }>({ alpha: 1 })
            .to({ alpha: 0 }, 250)
            .onUpdate(arg => {
                item.uiObject.renderOpacity = arg.alpha
            })
            .onComplete(() => {
                this.noticeItemPool.giveBack(item)
            })
            .start()
    }

    @updater.updateByFrameInterval(15, 'update')
    private checkPendingNotice() {
        if (this.pendingQueue.length < 1) return
        this.insertPendingNotice(this.pendingQueue.shift())
    }
}

class SecondNoticeComponent implements NoticeComponent<SecondNoticeItem>{
    private static NoticeItemLifeTime = 2
    private static NoticeItemMaxCount = 4
    private static NoticeMoveStepCount = 11
    private static NoticeItemIntervalSpace = 77

    shield: boolean = false

    private noticeItemPool: UIPool<SecondNoticeItem>
    private visibleNotice: SecondNoticeItem[]
    private pendingQueue: InitSecondNoticeFunction[]
    private noticeCanvasHeight: number
    private targetCanvas: Canvas
    private insertItemTempLocation: Vector2

    init(targetCanvas: Canvas) {
        this.visibleNotice = []
        this.pendingQueue = []
        this.targetCanvas = targetCanvas
        this.noticeCanvasHeight = this.targetCanvas.size.y
        this.insertItemTempLocation = new Vector2()
        this.noticeItemPool = new UIPool(() => {
            let item = UIService.create(SecondNoticeItem)
            this.targetCanvas.addChild(item.uiObject)
            item.uiObject.size = new Vector2(250, 60)
            return item
        })
    }

    insert(initAction: InitSecondNoticeFunction) {
        if (this.shield) return

        this.pendingQueue.push(initAction)
    }

    private insertPendingNotice(initAction: InitSecondNoticeFunction) {
        // 超出显示长度,旧的元素隐藏
        if (this.visibleNotice.length >= SecondNoticeComponent.NoticeItemMaxCount) { this.fadeoutNoticeElement() }
        // 已显示元素上推
        for (let i = 0; i < this.visibleNotice.length; i++) {
            const element = this.visibleNotice[i]
            element.targetHeight =
                (this.noticeCanvasHeight - SecondNoticeComponent.NoticeItemIntervalSpace) -
                ((this.visibleNotice.length - i) * SecondNoticeComponent.NoticeItemIntervalSpace)
        }
        // 插入新的元素
        let recent = this.noticeItemPool.get()
        this.visibleNotice.push(recent)
        initAction(recent)
        recent.startEffect()
        recent.lifeTime = 0
        this.insertItemTempLocation.x = (this.targetCanvas.size.x / 2) - (recent.uiObject.size.x / 2)
        this.insertItemTempLocation.y = this.targetCanvas.size.y - SecondNoticeComponent.NoticeItemIntervalSpace
        recent.setLocation(this.insertItemTempLocation.x, this.insertItemTempLocation.y)
        recent.targetHeight = this.insertItemTempLocation.y
        recent.uiObject.renderOpacity = 0;
        // 插入动效
        new Tween<{ alpha: number }>({ alpha: 0 })
            .to({ alpha: 1 }, 250)
            .onUpdate(arg => {
                recent.uiObject.renderOpacity = arg.alpha;
            })
            .start()
    }

    update() {
        if (this.visibleNotice.length == 0) return
        for (let item of this.visibleNotice) { item.lifeTime += 0.03 }
        let first = this.visibleNotice[0]
        if (first.lifeTime >= SecondNoticeComponent.NoticeItemLifeTime) { this.fadeoutNoticeElement() }

        this.noticeItemPool.eachVisibleItem(item => {
            if (item.targetHeight >= item.position.y) return
            item.setLocation(item.position.x, item.position.y - SecondNoticeComponent.NoticeMoveStepCount)
        })
    }

    private fadeoutNoticeElement() {
        let item = this.visibleNotice.shift()
        new Tween<{ alpha: number }>({ alpha: 1 })
            .to({ alpha: 0 }, 250)
            .onUpdate(arg => {
                item.uiObject.renderOpacity = arg.alpha
            })
            .onComplete(() => {
                this.noticeItemPool.giveBack(item)
            })
            .start()
    }

    @updater.updateByFrameInterval(8, 'update')
    private checkPendingNotice() {
        if (this.pendingQueue.length < 1) return
        this.insertPendingNotice(this.pendingQueue.shift())
    }
}

class MiddleNoticeComponent implements NoticeComponent<MiddleNoticeItem>{
    private static NoticeItemLifeTime = 2
    private static NoticeItemMaxCount = 1

    private noticeItemPool: UIPool<MiddleNoticeItem>
    private visibleNotice: MiddleNoticeItem[]
    private pendingQueue: InitMiddleNoticeFunction[]
    private targetCanvas: Canvas

    shield: boolean = false

    init(targetCanvas: Canvas) {
        this.visibleNotice = []
        this.pendingQueue = []
        this.targetCanvas = targetCanvas

        this.noticeItemPool = new UIPool(() => {
            let item = UIService.create(MiddleNoticeItem)
            this.targetCanvas.addChild(item.uiObject)
            return item
        })
    }
    insert(initAction: (item: MiddleNoticeItem) => void) {
        if (this.shield) return
        this.pendingQueue.push(initAction)
    }
    update() {
        if (this.visibleNotice.length == 0) return
        for (let item of this.visibleNotice) { item.lifeTime += 0.03 }
        let first = this.visibleNotice[0]
        if (first.lifeTime >= MiddleNoticeComponent.NoticeItemLifeTime) { this.fadeoutNoticeElement() }
    }

    private fadeoutNoticeElement() {
        let item = this.visibleNotice.shift()
        item.endEffect();
        new Tween<{ alpha: number }>({ alpha: 1 })
            .to({ alpha: 0 }, 50)
            .onUpdate(arg => {
                item.uiObject.renderOpacity = arg.alpha
            })
            .onComplete(() => {
                this.noticeItemPool.giveBack(item)
            })
            .start()
    }

    private insertPendingNotice(initAction: InitMiddleNoticeFunction) {
        if (this.visibleNotice.length >= MiddleNoticeComponent.NoticeItemMaxCount) { this.fadeoutNoticeElement() }

        // 插入新的元素
        let recent = this.noticeItemPool.get()
        this.visibleNotice.push(recent)
        initAction(recent)
        recent.startEffect()
        recent.lifeTime = 0
    }


    @updater.updateByFrameInterval(8, 'update')
    private checkPendingNotice() {
        if (this.pendingQueue.length < 1) return
        this.insertPendingNotice(this.pendingQueue.shift())
    }

}

class NoticeView extends NoticeView_Generate {
    topNoticeComponent: TopNoticeComponent
    secondNoticeComponent: SecondNoticeComponent
    middleNoticeComponent: MiddleNoticeComponent

    onStart() {
        this.topNoticeComponent = new TopNoticeComponent()
        this.topNoticeComponent.init(this.con_top_notice)
        this.secondNoticeComponent = new SecondNoticeComponent()
        this.secondNoticeComponent.init(this.con_second_notice)
        this.middleNoticeComponent = new MiddleNoticeComponent()
        this.middleNoticeComponent.init(this.con_middle_notice)

        this.canUpdate = true
        this.layer = UILayerSystem
    }

    onUpdate() {
        this.topNoticeComponent.update()
        this.secondNoticeComponent.update()
        this.middleNoticeComponent.update()
    }
}

const upP1 = new Vector2(0, -23)
const upP2 = new Vector2(0, -41)
const downP1 = new Vector2(0, 32)
const downP2 = new Vector2(0, 55)

class TopNoticeItem extends TopNoticeItem_Generate {
    lifeTime: number
    targetHeight: number
    effTween: Tween<{ x: number }>
    position: Vector2

    onStart() {
        this.effTween = new Tween({ x: 1 }).to({ x: 0 }).onStart(() => {
            this.eff.visibility = SlateVisibility.Visible
            this.eff_line_1.visibility = SlateVisibility.Visible
            this.eff_line_1_1.visibility = SlateVisibility.Visible
        }).onUpdate(obj => {
            this.eff.renderOpacity = obj.x
            this.eff_line_1.position = Vector2.lerp(upP1, upP2, 1 - obj.x)
            this.eff_line_1_1.position = Vector2.lerp(downP1, downP2, 1 - obj.x)
            this.eff_line_1.renderOpacity = obj.x
            this.eff_line_1_1.renderOpacity = obj.x
        }).onComplete(() => {
            this.eff.visibility = SlateVisibility.Hidden
            this.eff_line_1.visibility = SlateVisibility.Hidden
            this.eff_line_1_1.visibility = SlateVisibility.Hidden
            this.eff_line_1.position = upP1.clone()
            this.eff_line_1_1.position = downP1.clone()
        }).onStop(() => {
            this.eff.visibility = SlateVisibility.Hidden
            this.eff_line_1.visibility = SlateVisibility.Hidden
            this.eff_line_1_1.visibility = SlateVisibility.Hidden
            this.eff_line_1.position = upP1.clone()
            this.eff_line_1_1.position = downP1.clone()
        }).easing(Easing.Sinusoidal.Out)
    }

    setLocation(x: number, y: number) {
        if (!this.position) {
            this.position = new Vector2(x, y)
        }
        else {
            this.position.x = x
            this.position.y = y
        }
        this.uiObject.position = this.position
    }

    setInfo(context: string, txtHex: string) {
        this.txt_context.fontColor = LinearColor.colorHexToLinearColor(txtHex);
        this.txt_context.text = context
    }

    playEffect() {
        this.stopEffect()
        this.effTween.start()
    }

    stopEffect() {
        if (this.effTween.isPlaying()) {
            this.effTween.stop()
        }
    }
}

class SecondNoticeItem extends SecondNoticeItem_Generate {
    lifeTime: number
    targetHeight: number
    position: Vector2 = Vector2.zero;
    effTween: Tween<{ x: number }>
    private static startPoint: Vector2 = new Vector2(-60, -30)
    private static endPoint: Vector2 = new Vector2(350, -30)

    onStart() {
        this.effTween = new Tween({ x: 0 }).to({ x: 1 }, 500).onStart(() => {
            this.effect.position = SecondNoticeItem.startPoint
        }).onUpdate(obj => {
            this.effect.position = Vector2.lerp(SecondNoticeItem.startPoint, SecondNoticeItem.endPoint, obj.x)
        })
    }

    setInfoWithAssetsIcon(icon: string, context: string) {
        util.loadAssetsIcon(icon, this.icon)
        this.txt_context.text = context
    }

    setInfo(icon: string, context: string) {
        this.icon.imageGuid = icon
        this.txt_context.text = context
    }

    setLocation(x: number, y: number) {
        if (!this.position) {
            this.position = new Vector2(x, y)
        }
        else {
            this.position.x = x
            this.position.y = y
        }
        this.uiObject.position = this.position
    }

    startEffect() {
        this.effTween.start()
    }
}

class MiddleNoticeItem extends MiddleNoticeItem_Generate {
    effTween: Tween<{ x: number }>
    startTween: Tween<{ alpha: number }>
    lifeTime: number

    private static startPoint: Vector2 = new Vector2(-90, -160)
    private static endPoint: Vector2 = new Vector2(225, 100)
    onStart() {
        this.effTween = new Tween({ x: 0 }).to({ x: 1 }, 500).onStart(() => {
            // this.effect.position = MiddleNoticeItem.startPoint
        }).onUpdate(obj => {
            // this.effect.position = Vector2.lerp(MiddleNoticeItem.startPoint, MiddleNoticeItem.endPoint, obj.x)
        })

        // 插入动效
        this.startTween = new Tween<{ alpha: number }>({ alpha: 0 })
            .to({ alpha: 1 }, 250)
            .onUpdate(arg => {
                this.uiObject.renderOpacity = arg.alpha;
            })
            .start()
    }

    setItemInfo(itemInfo: ItemBaseInfo) {
        
    }

    startEffect() {
        this.effTween.start()
        this.startTween.start()
    }

    endEffect() {
        this.effTween.end();
        this.startTween.end();
        this.uiObject.renderOpacity = 1;
        // this.effect.position = MiddleNoticeItem.endPoint;
    }
}